﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Sofire.Data;
using System.Threading;

namespace OracleExpHelper
{
    public partial class FormExport : Form
    {
        public FormExport()
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
            this.DoubleBuffered = true;
        }

        private void CheckItems(bool checkedState)
        {
            for(int i = 0 ; i < clbox_Tables.Items.Count ; i++)
            {
                clbox_Tables.SetItemChecked(i, checkedState);
            }
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            btn_Refresh_Click(this, e);
        }

        private void btn_Refresh_Click(object sender, EventArgs e)
        {
            this.Text = "未连接...";
            clbox_Tables.BeginUpdate();
            clbox_Tables.Items.Clear();
            try
            {
                using(var context = Program.Database.Context)
                {
                    var type = typeof(OracleObjectType);
                    var names = Enum.GetNames(type);
                    var values = Enum.GetValues(type);
                    for(int i = 0 ; i < names.Length ; i++)
                    {
                        var r1 = context.ExecuteReader((reader) =>
                        {
                            while(reader.Read())
                            {
                                clbox_Tables.Items.Add(new OracleObjectItem(reader.GetString(0), (OracleObjectType)values.GetValue(i)));
                            }
                        }, "SELECT " + names[i] + "_NAME FROM USER_" + names[i] + "S ORDER BY " + names[i] + "_NAME");
                        if(!this.IsSuccess(r1)) return;
                    }

                    var r2 = context.ExecuteScalar("SELECT USER FROM DUAL");
                    if(!this.IsSuccess(r2)) return;
                    this.Text = "ORACLE Export Helper - " + r2.Value;
                    tbox_User.Text = lbl_User.Text = r2.Value.ToString();
                }

            }
            finally
            {
                clbox_Tables.EndUpdate();
            }
        }

        private void btnSelectedAll_Click(object sender, EventArgs e)
        {
            this.CheckItems(true);
        }

        private void btn_UnselectedAll_Click(object sender, EventArgs e)
        {
            this.CheckItems(false);
        }

        private void SetProgressMaxValue(int value)
        {
            if(progressBar1.InvokeRequired)
            {
                progressBar1.Invoke(new Action<int>(SetProgressMaxValue), value);
                return;
            }
            progressBar1.Maximum = value;
            progressBar1.Value = 0;
        }

        private void SetProgressValue(int value)
        {
            if(progressBar1.InvokeRequired)
            {
                progressBar1.Invoke(new Action<int>(SetProgressValue), value);
                return;
            }
            progressBar1.Value = value;
        }

        private void btn_ExtAll_Click(object sender, EventArgs e)
        {
            if(clbox_Tables.CheckedItems.Count == 0) return;
            var filename = this.CreateSaveFile();
            if(filename == null) return;
            btn_ExtAll.Visible = false;
            Thread t = new Thread(new ParameterizedThreadStart(ExportAll));
            t.IsBackground = true;
            t.Start(filename);
        }

        private void ExportAll(object e)
        {
            string filename = (string)e;
            using(FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                StreamWriter writer = new StreamWriter(fileStream);
                using(var context = Program.Database.Context)
                {
                    context.Open();
                    var checkedItems = clbox_Tables.CheckedItems;
                    this.SetProgressMaxValue(checkedItems.Count);
                    var collection = new List<string>(checkedItems.Count);
                    int index = 0;
                    foreach(OracleObjectItem objectItem in checkedItems)
                    {
                        var r = this.ExpObjectSchema(writer, context, objectItem.TypeName, objectItem.Name, collection);
                        if(!this.IsSuccess(r)) return;
                        this.SetProgressMaxValue(++index);
                    }
                }
            }
            this.BeginInvoke(new Action(() =>
            {
                MessageBox.Show("导出成功，请使用 WinRAR 进行数据压缩...");
                btn_ExtAll.Visible = true;
            }));
        }

        private string CreateSaveFile()
        {
            using(SaveFileDialog dialog = new SaveFileDialog())
            {
                dialog.Filter = "EXPD Files|*.expd";
                if(dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) return dialog.FileName;
            }
            return null;
        }

        private TableResult GetReferencesTables(QueryContext context, string tableName)
        {
            return context.ExecuteTable(@"SELECT DISTINCT c.TABLE_NAME
FROM ALL_CONSTRAINTS a 
JOIN USER_CONS_COLUMNS b ON a.CONSTRAINT_NAME=b.CONSTRAINT_NAME
JOIN ALL_CONSTRAINTS c ON a.R_CONSTRAINT_NAME=c.CONSTRAINT_NAME AND a.OWNER=c.OWNER
JOIN USER_CONS_COLUMNS e ON e.CONSTRAINT_NAME=c.CONSTRAINT_NAME
WHERE a.TABLE_NAME=:tableName AND a.CONSTRAINT_TYPE='R'", "tableName", tableName);
        }

        private StringResult GetObjectDDL(QueryContext context, string type, string name)
        {
            var r = context.ExecuteScalar("SELECT DBMS_METADATA.GET_DDL('" + type + "',:name) FROM DUAL", "name", name);
            if(r.IsFailed) return r.Exception;
            return new StringResult(Convert.ToString(r.Value));
        }

        private Result ExpObjectSchema(StreamWriter writer, QueryContext context, string typeName, string name, List<string> collection)
        {
            if(typeName == "TABLE")
            {
                var r = this.GetReferencesTables(context, name);
                if(r.IsFailed) return r;

                foreach(DataRow row in r.Value.Rows)
                {
                    var r2 = this.ExpObjectSchema(writer, context, typeName, Convert.ToString(row[0]), collection);
                    if(r2.IsFailed) return r2.Exception;
                }
                if(!collection.Contains(name))
                {
                    collection.Add(name);
                    using(var r3 = context.ExecuteTable("SELECT * FROM " + name))
                    {
                        if(r3.IsFailed) return r3.Exception;
                        if(r3.Value.Rows.Count > 0)
                        {
                            r3.Value.WriteXml(writer, XmlWriteMode.WriteSchema);
                            writer.WriteLine();
                            writer.WriteLine("<EOF>");
                            writer.WriteLine(typeName);
                            writer.WriteLine(name);
                            writer.Flush();
                        }
                    }
                }
            }
            else if(!collection.Contains(name))
            {
                var ddlR = this.GetObjectDDL(context, typeName, name);
                if(ddlR.IsFailed) return ddlR;
                collection.Add(name);
                var value = ddlR.Value;
                if(lbl_User.Text != tbox_User.Text)
                    value = value.Replace("\"" + lbl_User.Text + "\".", "\"" + tbox_User.Text + "\".");
                writer.Write(value);
                writer.WriteLine();
                writer.WriteLine("<EOF>");
                writer.WriteLine(typeName);
                writer.WriteLine(name);
                writer.Flush();
            }
            return Result.Successfully;
        }
    }
}
